﻿using Blog.Core.Common;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MyApp.Common;
using RedLockNet;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;

namespace MyApp.WebApi.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class RedisController : ControllerBase
    {
        private IRedisCacheManager RedisCacheManager;
        private readonly IDistributedLockFactory _distributedLockFactory;

        public RedisController(IRedisCacheManager RedisCacheManager, IDistributedLockFactory distributedLockFactory)
        {
            this.RedisCacheManager = RedisCacheManager;
            _distributedLockFactory = distributedLockFactory;

        }
        /// <summary>
        /// 查询Redis值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        [HttpGet]
        public string GetValue(string key)
        {

            return RedisCacheManager.GetValue(key);
        }
        /// <summary>
        /// 添加Redis键值对
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        [HttpPost]
        public void SetValue(string key, string value)
        {
            RedisCacheManager.Set(key, value, TimeSpan.FromMinutes(10));
        }

        /// <summary>
        /// 秒杀
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        [HttpPost]
        public void StockChange(string key, string value)
        {
            //https://www.cnblogs.com/xiongze520/p/15176559.html
            string script = "return redis.call('set',KEYS[1],ARGV[1])";
            var keys = new string[] { key };
            var values = new string[] { value };
            RedisCacheManager.ExecLua(script, keys, values);
        }



        /// <summary>
        /// 分布式锁
        /// </summary>
        /// <returns></returns>
        [HttpGet("TestLock2")]
        public async Task<IActionResult> TestLock2()
        {
            //https://github.com/samcook/RedLock.net
            var resource = "the-thing-we-are-locking-on";  // 锁定的对象
            var expiry = TimeSpan.FromSeconds(30);  //  锁定过期时间，锁区域内的逻辑执行如果超过过期时间，锁将被释放
            var wait = TimeSpan.FromSeconds(10);   //等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间,超过这个时间就不会等待锁了,即 redLock.IsAcquired 为false 
            var retry = TimeSpan.FromSeconds(1); //等待时间内，间隔多久尝试获取一次，如果超过上面设置的等待时间还没有获取到锁，也不会等待锁，即redLock.IsAcquired 为false 

            await using (var redLock = await _distributedLockFactory.CreateLockAsync(resource, expiry, wait, retry)) // 这里也可以不使用 wait 和 retry ，如果不使用当出现锁被占用就直接跳过，不会等待
            {
                // make sure we got the lock
                if (redLock.IsAcquired)
                {
                    var stopwatch = new Stopwatch();
                    stopwatch.Start();
                    var data = "";//new Test().Buy2();
                    stopwatch.Stop();
                    Console.WriteLine($"ThreadId:{Thread.CurrentThread.ManagedThreadId}, Result:{data}, Time:{stopwatch.ElapsedMilliseconds}");
                    return Ok($"ThreadId:{Thread.CurrentThread.ManagedThreadId}, Result:{data}, Time:{stopwatch.ElapsedMilliseconds}");
                }
                else
                {
                    Console.WriteLine("等待超时");
                    return Ok("等待超时");
                }
            }
        }

       //分布式锁的核心原理，其实就是获取锁，利用操作的原子性，
       //基于数据库的分布式锁，可以做sql判断插入，主键约束，插入成功，则认为是获取【Acquired】到了锁。
    }
}
